home *** CD-ROM | disk | FTP | other *** search
/ Aminet 30 / Aminet 30 (1999)(Schatztruhe)[!][Apr 1999].iso / Aminet / gfx / misc / gnuplot-3.7src.lha / gnuplot-3.7src / gnuplot-3.7.lha / gnuplot-3.7 / help.c < prev    next >
C/C++ Source or Header  |  1998-12-09  |  19KB  |  680 lines

  1. #ifndef lint
  2. static char *RCSid = "$Id: help.c,v 1.29 1998/04/14 00:15:36 drd Exp $";
  3. #endif
  4.  
  5. /* GNUPLOT - help.c */
  6.  
  7. /*[
  8.  * Copyright 1986 - 1993, 1998   Thomas Williams, Colin Kelley
  9.  *
  10.  * Permission to use, copy, and distribute this software and its
  11.  * documentation for any purpose with or without fee is hereby granted,
  12.  * provided that the above copyright notice appear in all copies and
  13.  * that both that copyright notice and this permission notice appear
  14.  * in supporting documentation.
  15.  *
  16.  * Permission to modify the software is granted, but not the right to
  17.  * distribute the complete modified source code.  Modifications are to
  18.  * be distributed as patches to the released version.  Permission to
  19.  * distribute binaries produced by compiling modified sources is granted,
  20.  * provided you
  21.  *   1. distribute the corresponding source modifications from the
  22.  *    released version in the form of a patch file along with the binaries,
  23.  *   2. add special version identification to distinguish your version
  24.  *    in addition to the base release version number,
  25.  *   3. provide your name and address as the primary contact for the
  26.  *    support of your modified version, and
  27.  *   4. retain our contact information in regard to use of the base
  28.  *    software.
  29.  * Permission to distribute the released version of the source code along
  30.  * with corresponding source modifications in the form of a patch file is
  31.  * granted with same provisions 2 through 4 for binary distributions.
  32.  *
  33.  * This software is provided "as is" without express or implied warranty
  34.  * to the extent permitted by applicable law.
  35. ]*/
  36.  
  37. #include "plot.h"
  38.  
  39. #define    SAME    0        /* for strcmp() */
  40.  
  41. #include "help.h"        /* values passed back */
  42.  
  43. void int_error __PROTO((char str[], int t_num));
  44.  
  45. #if defined(__EMX__) || defined(DJGPP) || defined(DOS386)
  46. /* we have plenty of memory under __EMX__ or DJGPP */
  47. # ifdef MSDOS
  48. #  undef MSDOS
  49. # endif
  50. #endif
  51.  
  52. /* 
  53.  ** help -- help subsystem that understands defined keywords
  54.  **
  55.  ** Looks for the desired keyword in the help file at runtime, so you
  56.  ** can give extra help or supply local customizations by merely editing
  57.  ** the help file.
  58.  **
  59.  ** The original (single-file) idea and algorithm is by John D. Johnson,
  60.  ** Hewlett-Packard Company.  Thanx and a tip of the Hatlo hat!
  61.  **
  62.  ** Much extension by David Kotz for use in gnutex, and then in gnuplot.
  63.  ** Added output paging support, both unix and builtin. Rewrote completely
  64.  ** to read helpfile into memory, avoiding reread of help file. 12/89.
  65.  **
  66.  ** Modified by Russell Lang to avoid reading completely into memory
  67.  ** if MSDOS defined.  This uses much less memory.  6/91
  68.  **
  69.  ** The help file looks like this (the question marks are really in column 1):
  70.  **
  71.  **   ?topic
  72.  **   This line is printed when the user wants help on "topic".
  73.  **   ?keyword
  74.  **   ?Keyword
  75.  **   ?KEYWORD
  76.  **   These lines will be printed on the screen if the user wanted
  77.  **   help on "keyword", "Keyword", or "KEYWORD".  No casefolding is
  78.  **   done on the keywords.
  79.  **   ?subject
  80.  **   ?alias
  81.  **   This line is printed for help on "subject" and "alias".
  82.  **   ?
  83.  **   ??
  84.  **   Since there is a null keyword for this line, this section
  85.  **   is printed when the user wants general help (when a help
  86.  **   keyword isn't given).  A command summary is usually here.
  87.  **   Notice that the null keyword is equivalent to a "?" keyword
  88.  **   here, because of the '?' and '??' topic lines above.
  89.  **   If multiple keywords are given, the first is considered the 
  90.  **   'primary' keyword. This affects a listing of available topics.
  91.  **   ?last-subject
  92.  **   Note that help sections are terminated by the start of the next
  93.  **   '?' entry or by EOF.  So you can't have a leading '?' on a line
  94.  **   of any help section.  You can re-define the magic character to
  95.  **   recognize in column 1, though, if '?' is too useful.  (Try ^A.)
  96.  */
  97.  
  98. #define    KEYFLAG    '?'        /* leading char in help file topic lines */
  99.  
  100. /*
  101.  ** Calling sequence:
  102.  **   int result;             # 0 == success
  103.  **   char *keyword;          # topic to give help on
  104.  **   char *pathname;         # path of help file
  105.  **   int subtopics;          # set to TRUE if only subtopics to be listed
  106.  **                           # returns TRUE if subtopics were found
  107.  **   result = help(keyword, pathname, &subtopics);
  108.  ** Sample:
  109.  **   cmd = "search\n";
  110.  **   helpfile = "/usr/local/lib/program/program.help";
  111.  **   subtopics = FALSE;
  112.  **   if (help(cmd, helpfile, &subtopics) != H_FOUND)
  113.  **           printf("Sorry, no help for %s", cmd);
  114.  **
  115.  **
  116.  ** Speed this up by replacing the stdio calls with open/close/read/write.
  117.  */
  118. #ifdef WDLEN
  119. # define PATHSIZE WDLEN
  120. #else
  121. # define PATHSIZE BUFSIZ
  122. #endif
  123.  
  124. typedef struct line_s LINEBUF;
  125. struct line_s {
  126.     char *line;            /* the text of this line */
  127.     LINEBUF *next;        /* the next line */
  128. };
  129.  
  130. typedef struct linkey_s LINKEY;
  131. struct linkey_s {
  132.     char *key;            /* the name of this key */
  133.     long pos;            /* ftell position */
  134.     LINEBUF *text;        /* the text for this key */
  135.     TBOOLEAN primary;        /* TRUE -> is a primary name for a text block */
  136.     LINKEY *next;        /* the next key in linked list */
  137. };
  138.  
  139. typedef struct key_s KEY;
  140. struct key_s {
  141.     char *key;            /* the name of this key */
  142.     long pos;            /* ftell position */
  143.     LINEBUF *text;        /* the text for this key */
  144.     TBOOLEAN primary;        /* TRUE -> is a primary name for a text block */
  145. };
  146. static LINKEY *keylist = NULL;    /* linked list of keys */
  147. static KEY *keys = NULL;    /* array of keys */
  148. static int keycount = 0;    /* number of keys */
  149. static FILE *helpfp = NULL;
  150.  
  151. static int LoadHelp __PROTO((char *path));
  152. static void sortkeys __PROTO((void));
  153. static int keycomp __PROTO((struct key_s * a, struct key_s * b));
  154. static LINEBUF *storeline __PROTO((char *text));
  155. static LINKEY *storekey __PROTO((char *key));
  156. static KEY *FindHelp __PROTO((char *keyword));
  157. static TBOOLEAN Ambiguous __PROTO((struct key_s * key, int len));
  158.  
  159. /* Help output */
  160. static void PrintHelp __PROTO((struct key_s * key, int *subtopics));
  161. static void ShowSubtopics __PROTO((struct key_s * key, int *subtopics));
  162.  
  163. #if defined(PIPES)
  164. static FILE *outfile;        /* for unix pager, if any */
  165. #endif
  166. static int pagelines;        /* count for builtin pager */
  167. #define SCREENSIZE 24        /* lines on screen (most have at least 24) */
  168.  
  169. /* help:
  170.  * print a help message 
  171.  * also print available subtopics, if subtopics is TRUE
  172.  */
  173. int help(keyword, path, subtopics)
  174. char *keyword;            /* on this topic */
  175. char *path;            /* from this file */
  176. TBOOLEAN *subtopics;        /* (in) - subtopics only? */
  177.                 /* (out) - are there subtopics? */
  178. {
  179.     static char oldpath[PATHSIZE] = "";        /* previous help file */
  180.     int status;            /* result of LoadHelp */
  181.     KEY *key;            /* key that matches keyword */
  182.  
  183.     /*
  184.      ** Load the help file if necessary (say, first time we enter this routine,
  185.      ** or if the help file changes from the last time we were called).
  186.      ** Also may occur if in-memory copy was freed.
  187.      ** Calling routine may access errno to determine cause of H_ERROR.
  188.      */
  189.     errno = 0;
  190.     if (strncmp(oldpath, path, PATHSIZE) != SAME)
  191.     FreeHelp();
  192.     if (keys == NULL) {
  193.     status = LoadHelp(path);
  194.     if (status == H_ERROR)
  195.         return (status);
  196.  
  197.     /* save the new path in oldpath */
  198.     safe_strncpy(oldpath, path, PATHSIZE);
  199.     }
  200.     /* look for the keyword in the help file */
  201.     key = FindHelp(keyword);
  202.     if (key != NULL) {
  203.     /* found the keyword: print help and return */
  204.     PrintHelp(key, subtopics);
  205.     status = H_FOUND;
  206.     } else {
  207.     status = H_NOTFOUND;
  208.     }
  209.  
  210.     return (status);
  211. }
  212.  
  213. /* we only read the file once, into memory
  214.  * except for MSDOS when we don't read all the file -
  215.  * just the keys and location of the text
  216.  */
  217. static int LoadHelp(path)
  218. char *path;
  219. {
  220.     LINKEY *key;        /* this key */
  221.     long pos = 0;        /* ftell location within help file */
  222.     char buf[BUFSIZ];        /* line from help file */
  223.     LINEBUF *head;        /* head of text list  */
  224.     LINEBUF *firsthead = NULL;
  225.     TBOOLEAN primary;        /* first ? line of a set is primary */
  226.     TBOOLEAN flag;
  227.  
  228.     if ((helpfp = fopen(path, "r")) == NULL) {
  229.     /* can't open help file, so error exit */
  230.     return (H_ERROR);
  231.     }
  232.     /*
  233.      ** The help file is open.  Look in there for the keyword.
  234.      */
  235.     if (!fgets(buf, BUFSIZ - 1, helpfp) || *buf != KEYFLAG)
  236.     return (H_ERROR);    /* it is probably not the .gih file */
  237.  
  238.     while (!feof(helpfp)) {
  239.     /*
  240.      ** Make an entry for each synonym keyword
  241.      */
  242.     primary = TRUE;
  243.     while (buf[0] == KEYFLAG) {
  244.         key = storekey(buf + 1);    /* store this key */
  245.         key->primary = primary;
  246.         key->text = NULL;    /* fill in with real value later */
  247.         key->pos = 0;    /* fill in with real value later */
  248.         primary = FALSE;
  249.         pos = ftell(helpfp);
  250.         if (fgets(buf, BUFSIZ - 1, helpfp) == (char *) NULL)
  251.         break;
  252.     }
  253.     /*
  254.      ** Now store the text for this entry.
  255.      ** buf already contains the first line of text.
  256.      */
  257. #ifndef MSDOS
  258.     firsthead = storeline(buf);
  259.     head = firsthead;
  260. #endif
  261.     while ((fgets(buf, BUFSIZ - 1, helpfp) != (char *) NULL)
  262.            && (buf[0] != KEYFLAG)) {
  263. #ifndef MSDOS
  264.         /* save text line */
  265.         head->next = storeline(buf);
  266.         head = head->next;
  267. #endif
  268.     }
  269.     /* make each synonym key point to the same text */
  270.     do {
  271.         key->pos = pos;
  272.         key->text = firsthead;
  273.         flag = key->primary;
  274.         key = key->next;
  275.     } while (flag != TRUE && key != NULL);
  276.     }
  277. #ifndef MSDOS
  278.     (void) fclose(helpfp);
  279. #endif
  280.  
  281.     /* we sort the keys so we can use binary search later */
  282.     sortkeys();
  283.     return (H_FOUND);        /* ok */
  284. }
  285.  
  286. /* make a new line buffer and save this string there */
  287. static LINEBUF *
  288.  storeline(text)
  289. char *text;
  290. {
  291.     LINEBUF *new;
  292.  
  293.     new = (LINEBUF *) malloc(sizeof(LINEBUF));
  294.     if (new == NULL)
  295.     int_error("not enough memory to store help file", -1);
  296.     if (text != NULL) {
  297.     new->line = (char *) malloc((unsigned int) (strlen(text) + 1));
  298.     if (new->line == NULL)
  299.         int_error("not enough memory to store help file", -1);
  300.     (void) strcpy(new->line, text);
  301.     } else
  302.     new->line = NULL;
  303.  
  304.     new->next = NULL;
  305.  
  306.     return (new);
  307. }
  308.  
  309. /* Add this keyword to the keys list, with the given text */
  310. static LINKEY *
  311.  storekey(key)
  312. char *key;
  313. {
  314.     LINKEY *new;
  315.  
  316.     key[strlen(key) - 1] = NUL;    /* cut off \n  */
  317.  
  318.     new = (LINKEY *) malloc(sizeof(LINKEY));
  319.     if (new == NULL)
  320.     int_error("not enough memory to store help file", -1);
  321.     new->key = (char *) malloc((unsigned int) (strlen(key) + 1));
  322.     if (new->key == NULL)
  323.     int_error("not enough memory to store help file", -1);
  324.     (void) strcpy(new->key, key);
  325.  
  326.     /* add to front of list */
  327.     new->next = keylist;
  328.     keylist = new;
  329.     keycount++;
  330.     return (new);
  331. }
  332.  
  333. /* we sort the keys so we can use binary search later */
  334. /* We have a linked list of keys and the number.
  335.  * to sort them we need an array, so we reform them into an array,
  336.  * and then throw away the list.
  337.  */
  338. static void sortkeys()
  339. {
  340.     LINKEY *p, *n;        /* pointers to linked list */
  341.     int i;            /* index into key array */
  342.  
  343.     /* allocate the array */
  344.     keys = (KEY *) malloc((unsigned int) ((keycount + 1) * sizeof(KEY)));
  345.     if (keys == NULL)
  346.     int_error("not enough memory to store help file", -1);
  347.  
  348.     /* copy info from list to array, freeing list */
  349.     for (p = keylist, i = 0; p != NULL; p = n, i++) {
  350.     keys[i].key = p->key;
  351.     keys[i].pos = p->pos;
  352.     keys[i].text = p->text;
  353.     keys[i].primary = p->primary;
  354.     n = p->next;
  355.     free((char *) p);
  356.     }
  357.  
  358.     /* a null entry to terminate subtopic searches */
  359.     keys[keycount].key = NULL;
  360.     keys[keycount].pos = 0;
  361.     keys[keycount].text = NULL;
  362.  
  363.     /* sort the array */
  364.     /* note that it only moves objects of size (two pointers + long + int) */
  365.     /* it moves no strings */
  366.     qsort((char *) keys, keycount, sizeof(KEY), (sortfunc) keycomp);
  367. }
  368.  
  369. static int keycomp(a, b)
  370. KEY *a, *b;
  371. {
  372.     return (strcmp(a->key, b->key));
  373. }
  374.  
  375. /* Free the help file from memory. */
  376. /* May be called externally if space is needed */
  377. void FreeHelp()
  378. {
  379.     int i;            /* index into keys[] */
  380.     LINEBUF *t, *next;
  381.  
  382.     if (keys == NULL)
  383.     return;
  384.  
  385.     for (i = 0; i < keycount; i++) {
  386.     free((char *) keys[i].key);
  387.     if (keys[i].primary)    /* only try to release text once! */
  388.         for (t = keys[i].text; t != NULL; t = next) {
  389.         free((char *) t->line);
  390.         next = t->next;
  391.         free((char *) t);
  392.         }
  393.     }
  394.     free((char *) keys);
  395.     keys = NULL;
  396.     keycount = 0;
  397. #ifdef MSDOS
  398.     (void) fclose(helpfp);
  399. #endif
  400. }
  401.  
  402. /* FindHelp:
  403.  *  Find the key that matches the keyword.
  404.  *  The keys[] array is sorted by key.
  405.  *  We could use a binary search, but a linear search will aid our
  406.  *  attempt to allow abbreviations. We search for the first thing that
  407.  *  matches all the text we're given. If not an exact match, then
  408.  *  it is an abbreviated match, and there must be no other abbreviated
  409.  *  matches -- for if there are, the abbreviation is ambiguous. 
  410.  *  We print the ambiguous matches in that case, and return not found.
  411.  */
  412. static KEY *            /* NULL if not found */
  413.  FindHelp(keyword)
  414. char *keyword;            /* string we look for */
  415. {
  416.     KEY *key;
  417.     int len = strlen(keyword);
  418.     int compare;
  419.  
  420.     for (key = keys, compare = 1; key->key != NULL && compare > 0; key++) {
  421.     compare = strncmp(keyword, key->key, len);
  422.     if (compare == 0)    /* we have a match! */
  423.         if (!Ambiguous(key, len)) {
  424.         /* non-ambiguous abbreviation */
  425.         (void) strcpy(keyword, key->key);    /* give back the full spelling */
  426.         return (key);    /* found!! */
  427.         }
  428.     }
  429.  
  430.     /* not found, or ambiguous */
  431.     return (NULL);
  432. }
  433.  
  434. /* Ambiguous:
  435.  * Check the key for ambiguity up to the given length.
  436.  * It is ambiguous if it is not a complete string and there are other
  437.  * keys following it with the same leading substring.
  438.  */
  439. static TBOOLEAN
  440.  Ambiguous(key, len)
  441. KEY *key;
  442. int len;
  443. {
  444.     char *first;
  445.     char *prev;
  446.     TBOOLEAN status = FALSE;    /* assume not ambiguous */
  447.     int compare;
  448.     int sublen;
  449.  
  450.     if (key->key[len] == NUL)
  451.     return (FALSE);
  452.  
  453.     for (prev = first = key->key, compare = 0, key++;
  454.      key->key != NULL && compare == 0; key++) {
  455.     compare = strncmp(first, key->key, len);
  456.     if (compare == 0) {
  457.         /* So this key matches the first one, up to len.
  458.          * But is it different enough from the previous one
  459.          * to bother printing it as a separate choice?
  460.          */
  461.         sublen = instring(prev + len, ' ');
  462.         if (strncmp(key->key, prev, len + sublen) != 0) {
  463.         /* yup, this is different up to the next space */
  464.         if (!status) {
  465.             /* first one we have printed is special */
  466.             fprintf(stderr,
  467.              "Ambiguous request '%.*s'; possible matches:\n",
  468.                 len, first);
  469.             fprintf(stderr, "\t%s\n", prev);
  470.             status = TRUE;
  471.         }
  472.         fprintf(stderr, "\t%s\n", key->key);
  473.         prev = key->key;
  474.         }
  475.     }
  476.     }
  477.  
  478.     return (status);
  479. }
  480.  
  481. /* PrintHelp:
  482.  * print the text for key
  483.  */
  484. static void PrintHelp(key, subtopics)
  485. KEY *key;
  486. TBOOLEAN *subtopics;        /* (in) - subtopics only? */
  487.                 /* (out) - are there subtopics? */
  488. {
  489.     LINEBUF *t;
  490. #ifdef MSDOS
  491.     char buf[BUFSIZ];        /* line from help file */
  492. #endif
  493.  
  494.     StartOutput();
  495.  
  496.     if (subtopics == NULL || !*subtopics) {
  497. #ifdef MSDOS
  498.     fseek(helpfp, key->pos, 0);
  499.     while ((fgets(buf, BUFSIZ - 1, helpfp) != (char *) NULL)
  500.            && (buf[0] != KEYFLAG)) {
  501.         OutLine(buf);
  502.     }
  503. #else
  504.     for (t = key->text; t != NULL; t = t->next)
  505.         OutLine(t->line);    /* print text line */
  506. #endif
  507.     }
  508.     ShowSubtopics(key, subtopics);
  509.     OutLine("\n");
  510.  
  511.     EndOutput();
  512. }
  513.  
  514.  
  515. /* ShowSubtopics:
  516.  *  Print a list of subtopic names
  517.  */
  518. #define PER_LINE 4
  519.  
  520. static void ShowSubtopics(key, subtopics)
  521. KEY *key;            /* the topic */
  522. TBOOLEAN *subtopics;        /* (out) are there any subtopics */
  523. {
  524.     int subt = 0;        /* printed any subtopics yet? */
  525.     KEY *subkey;        /* subtopic key */
  526.     int len;            /* length of key name */
  527.     char line[BUFSIZ];        /* subtopic output line */
  528.     char *start;        /* position of subname in key name */
  529.     int sublen;            /* length of subname */
  530.     int pos = 0;
  531.     int spacelen = 0;        /* Moved from inside for() loop */
  532.     char *prev = NULL;        /* the last thing we put on the list */
  533.  
  534.     *line = NUL;
  535.     len = strlen(key->key);
  536.  
  537.     for (subkey = key + 1; subkey->key != NULL; subkey++) {
  538.     int ispacelen = 0;
  539.     if (strncmp(subkey->key, key->key, len) == 0) {
  540.         /* find this subtopic name */
  541.         start = subkey->key + len;
  542.         if (len > 0) {
  543.         if (*start == ' ')
  544.             start++;    /* skip space */
  545.         else
  546.             break;    /* not the same topic after all  */
  547.         } else {
  548.         /* here we are looking for main topics */
  549.         if (!subkey->primary)
  550.             continue;    /* not a main topic */
  551.         }
  552.         sublen = instring(start, ' ');
  553.         if (prev == NULL || strncmp(start, prev, sublen) != 0) {
  554.         if (subt == 0) {
  555.             subt++;
  556.             if (len)
  557.             (void) sprintf(line, "\nSubtopics available for %s:\n",
  558.                        key->key);
  559.             else
  560.             (void) sprintf(line, "\nHelp topics available:\n");
  561.             OutLine(line);
  562.             *line = NUL;
  563.             pos = 0;
  564.         }
  565.         if (pos == PER_LINE) {
  566.             (void) strcat(line, "\n");
  567.             OutLine(line);
  568.             *line = NUL;
  569.             pos = 0;
  570.         }
  571.         /* adapted by DvdSchaaf */
  572.         {
  573. #define FIRSTCOL    6
  574. #define COLLENGTH    15
  575.  
  576.             if (pos == 0)
  577.             spacelen = FIRSTCOL;
  578.             for (ispacelen = 0;
  579.              ispacelen < spacelen; ispacelen++)
  580.             (void) strcat(line, " ");
  581.             /* commented out *
  582.                (void) strcat(line, "\t");
  583.              */
  584.             (void) strncat(line, start, sublen);
  585.             spacelen = COLLENGTH - sublen;
  586.             if (spacelen <= 0)
  587.             spacelen = 1;
  588.         }
  589.         pos++;
  590.         prev = start;
  591.         }
  592.     } else {
  593.         /* new topic */
  594.         break;
  595.     }
  596.     }
  597.  
  598.     /* put out the last line */
  599.     if (subt > 0 && pos > 0) {
  600.     (void) strcat(line, "\n");
  601.     OutLine(line);
  602.     }
  603. /*
  604.    if (subt == 0) {
  605.    OutLine("\n");
  606.    OutLine("No subtopics available\n");
  607.    }
  608.  */
  609.  
  610.     if (subtopics)
  611.     *subtopics = (subt != 0);
  612. }
  613.  
  614.  
  615. /* StartOutput:
  616.  * Open a file pointer to a pipe to user's $PAGER, if there is one,
  617.  * otherwise use our own pager.
  618.  */
  619. void StartOutput()
  620. {
  621. #if defined(PIPES)
  622.     char *pager_name = getenv("PAGER");
  623.  
  624.     if (pager_name != NULL && *pager_name != NUL)
  625.     if ((outfile = popen(pager_name, "w")) != (FILE *) NULL)
  626.         return;        /* success */
  627.     outfile = stderr;
  628.     /* fall through to built-in pager */
  629. #endif
  630.  
  631.     /* built-in pager */
  632.     pagelines = 0;
  633. }
  634.  
  635. #if defined(ATARI) || defined(MTOS)
  636. # ifndef READLINE
  637. #  error cannot compile atari versions without -DREADLINE
  638. # endif
  639. #endif
  640.  
  641. /* write a line of help output  */
  642. /* line should contain only one \n, at the end */
  643. void OutLine(line)
  644. char *line;
  645. {
  646.     int c;            /* dummy input char */
  647. #if defined(PIPES)
  648.     if (outfile != stderr) {
  649.     fputs(line, outfile);
  650.     return;
  651.     }
  652. #endif
  653.  
  654.     /* built-in dumb pager */
  655.     /* leave room for prompt line */
  656.     if (pagelines >= SCREENSIZE - 2) {
  657.     fputs("Press return for more: ", stderr);
  658. #if defined(ATARI) || defined(MTOS)
  659.     do
  660.         c = tos_getch();
  661.     while (c != '\x04' && c != '\r' && c != '\n');
  662. #else
  663.     do
  664.         c = getchar();
  665.     while (c != EOF && c != '\n');
  666. #endif
  667.     pagelines = 0;
  668.     }
  669.     fputs(line, stderr);
  670.     pagelines++;
  671. }
  672.  
  673. void EndOutput()
  674. {
  675. #if defined(PIPES)
  676.     if (outfile != stderr)
  677.     (void) pclose(outfile);
  678. #endif
  679. }
  680.